using System;
using System.Reflection;
using System.IO;
using System.Threading;
using System.Xml;
using System.Collections;
using SharpObjects.Configuration;
using SharpObjects.Configuration.ConfigurationProviders;
using System.Web;
using SharpObjects.Interfaces;
using SharpObjects.PathMapping;

namespace SharpObjects.Logging.LogTargets
{
    public abstract class FileLogTargetBase : LogTargetBase
    {
        protected const string defaultExtension = ".log";

        protected string logDirectory = DefaultPathMapper.GetApplicationDataDirectory();
        protected string logFileNameBase = DefaultPathMapper.ApplicationName;
        protected string fileExtension = defaultExtension;
        protected string logFileName;
        protected string root;

        protected IPathMapper pathMapper = PathMapperFactory.PathMapper;
        protected TimeSpan keepHistoryInterval = TimeSpan.MaxValue;
        protected bool mapToAppData = false;
        protected bool seekToEndBeforeWrite = false;

        #region INITIALIZATION

        protected void InitLogFileName()
        {
            this.logFileName = string.Format("{0}\\{1}{2}", this.logDirectory, this.logFileNameBase, this.fileExtension);
        }

        protected void InitExtension(string extension)
        {
            if (!extension.StartsWith("."))
            {
                this.fileExtension = "." + extension;
            }
            else
            {
                this.fileExtension = extension;
            }
        }

        #endregion //INITIALIZATION

        #region CONSTRUCTORS

        protected FileLogTargetBase()
        {
            CreateMutex(this.logFileNameBase);
            InitLogFileName();
        }

        protected FileLogTargetBase(string logDirectory)
        {
            this.logDirectory = this.pathMapper.MapApplicationDataPath(logDirectory);
            CreateMutex(this.logFileNameBase);
            InitLogFileName();
        }

        protected FileLogTargetBase(string logDirectory, string logFileNameBase)
        {
            this.logDirectory = this.pathMapper.MapApplicationDataPath(logDirectory);
            this.logFileNameBase = logFileNameBase;
            CreateMutex(this.logFileNameBase);
            InitLogFileName();
        }

        protected FileLogTargetBase(string logDirectory, string logFileNameBase, string extension)
        {
            this.logDirectory = this.pathMapper.MapApplicationDataPath(logDirectory);
            this.logFileNameBase = logFileNameBase;
            CreateMutex(this.logFileNameBase);
            InitExtension(extension);
            InitLogFileName();
        }

        protected FileLogTargetBase(string logDirectory, string logFileNameBase, string extension, int nMaxSize)
        {
            this.logDirectory = this.pathMapper.MapApplicationDataPath(logDirectory);
            this.logFileNameBase = logFileNameBase;
            CreateMutex(this.logFileNameBase);
            InitExtension(extension);
            InitLogFileName();
            this.maxSize = nMaxSize;
        }

        #endregion //CONSTRUCTORS


        /*
          <XXX_log_target is_enabled="true" [id="text_log_target"]>
            <formatter pathref="//logging/formatters/default_log_formatter" />
            <directory map_to_app_data="false" root="MyCompany">Logs</directory>
            <file_name_base>SharpObjects.Logging_Test_FastText</file_name_base>
            <file_extension>log</file_extension>
            <log_max_size units="mb">2</file_max_size>
            <keep_history_for units="day">30</keep_history_for>
            <origin_filter>
              <include>
                <starts_with>aaa</starts_with>
                <starts_with>bbb</starts_with>
                <contains>ccc</contains>
                <contains>ddd</contains>
                <ends_with>eee</ends_with>
                <ends_with>fff</ends_with>
              </include>
              <exclude>
                <starts_with>ggg</starts_with>
                <starts_with>hhh</starts_with>
                <contains>iii</contains>
                <contains>jjj</contains>
                <ends_with>kkk</ends_with>
                <ends_with>lll</ends_with>
              </exclude>
            </origin_filter>
           </XXX_log_target>

        */

        public override void Initialize(XmlElement initializationElement)
        {
            base.Initialize(initializationElement);

            IConfigurationProvider init = new XmlConfigurationProvider(initializationElement);

            this.logDirectory = init.GetString("directory", this.logDirectory);
            this.mapToAppData = init.GetBoolean("directory/@map_to_app_data", false);
            this.root = init.GetString("directory/@root", null);

            if (this.mapToAppData)
            {
                if (this.root == null)
                {
                    this.logDirectory = this.pathMapper.MapApplicationDataPath(this.logDirectory);
                }
                else
                {
                    if(!Path.IsPathRooted(this.logDirectory))
                    {
                        this.logDirectory = this.pathMapper.MapApplicationDataPath(this.root, this.logDirectory);
                    }
                }
            }
            else
            {
                this.logDirectory = this.pathMapper.MapAssemblyBasePath(this.logDirectory);
            }

            this.seekToEndBeforeWrite = init.GetBoolean("seek_to_end_before_write", false);

            this.logFileNameBase = init.GetString("file_name_base", this.logFileNameBase);
            CreateMutex(this.logFileNameBase);
            string extension = init.GetString("file_extension", this.fileExtension);

            if (extension != string.Empty)
            {
                if (extension.StartsWith("."))
                {
                    this.fileExtension = extension;
                }
                else
                {
                    this.fileExtension = "." + extension;
                }
            }

            InitializeHistoryInterval(init);
            InitLogFileName();
        }



        protected void InitializeHistoryInterval(IConfigurationProvider init)
        {
            int historyInterval = init.GetInteger("keep_history_for", int.MaxValue);

            if (historyInterval == int.MaxValue)
            {
                this.keepHistoryInterval = TimeSpan.MaxValue;
            }

            string timeUnits = init.GetString("keep_history_for/@units", "day");

            switch (timeUnits)
            {
                case "min":
                    this.keepHistoryInterval = TimeSpan.FromMinutes(historyInterval);
                    break;
                case "mins":
                    this.keepHistoryInterval = TimeSpan.FromMinutes(historyInterval);
                    break;
                case "hour":
                    this.keepHistoryInterval = TimeSpan.FromHours(historyInterval);
                    break;
                case "hours":
                    this.keepHistoryInterval = TimeSpan.FromHours(historyInterval);
                    break;
                case "day":
                    this.keepHistoryInterval = TimeSpan.FromDays(historyInterval);
                    break;
                case "days":
                    this.keepHistoryInterval = TimeSpan.FromDays(historyInterval);
                    break;
                default:
                    break;
            }
        }

        protected void CleanupHistory()
        {
            if (this.keepHistoryInterval == TimeSpan.MaxValue)
            {
                return;
            }

            Thread cleanupThread = new Thread(new ThreadStart(CleanupThread));
            cleanupThread.IsBackground = true;
            cleanupThread.Start();
        }

        protected void CleanupThread()
        {
            try
            {
                DirectoryInfo directoryInfo = new DirectoryInfo(this.logDirectory);

                FileInfo[] files = directoryInfo.GetFiles(this.logFileNameBase + "_(*)" + this.fileExtension);

                foreach (FileInfo fileInfo in files)
                {
                    if (fileInfo.LastWriteTime < (DateTime.Now - this.keepHistoryInterval))
                    {
                        fileInfo.Delete();
                    }
                }
            }
            catch (Exception exception)
            {
                Log.AddErrorMessage(exception);
            }
        }

        protected void Rename()
        {
            Close();
            // rename the existing file
            DateTime dt = DateTime.Now;
            string targetFileName = string.Format("{0}\\{1}_({2}-{3}-{4}_{5}.{6}.{7}){8}", this.logDirectory, this.logFileNameBase, dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, this.fileExtension);
            File.Move(this.logFileName, targetFileName);
        }

        protected void CreateNew()
        {
            if(!Directory.Exists(this.logDirectory))
            {
                Directory.CreateDirectory(this.logDirectory);
            }

            FileStream fs = File.Create(this.logFileName);
            fs.Close();

            File.SetCreationTime(this.logFileName, DateTime.Now); // fixes NTFS Tonneling "feature". Article: Q172190
        }

        public override void Release()
        {
            Close();
        }
    }
}
